/*
 ISpeLLController.m
 
 Created by cool8jay on 2/16/10.
 Copyright 2010 cool8jay@qq.com All rights reserved.
 */
#import "ISpeLLController.h"
#import "PreferenceController.h"

@interface ISpeLLController (PrivateAPI)

- (void)fillEnglishExplanation:(DOMDocument*)domDocument;

@end

@implementation ISpeLLController

@synthesize spellingProgressIndicator;
@synthesize dictionaryWords;
@synthesize mainContentWindow;
@synthesize toggleAlwaysOnTopMenuItem;
@synthesize statusBarMenu;
@synthesize statusItem;
@synthesize pinButton;

/*
 Used only to set default preference value for the application.
 */
+ (void)initialize
{
	NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
	
	[defaultValues setObject:[NSNumber numberWithBool:YES]
					  forKey:C8JShowTipKey];
	[defaultValues setObject:[NSNumber numberWithBool:YES]
					  forKey:C8JShowDockIconKey];
	[defaultValues setObject:[NSNumber numberWithBool:NO]
					  forKey:C8JAlwaysOnTopKey];
	/*
	 Enable automatic update.
	 */
	[defaultValues setObject:[NSNumber numberWithBool:YES]
					  forKey:@"SUEnableAutomaticChecks"];
	
	/*
	 A decent starting value for the main hotkey is control-F12
	 */
	[defaultValues setObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithInt:111],[NSNumber numberWithInt:8650752],nil] 
														 forKeys:[NSArray arrayWithObjects:@"keyCode",@"modifierFlags",nil]]
					  forKey:C8JShowMainWindowShortcutKey];
	
	[[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues];
}

- (id) init
{
	self = [super init];
	if (self != nil) {
		resultWords = [NSMutableArray new];
		finderQueue = [NSOperationQueue new];
		
		/*
		 Read the 'words' file and convert it into an array of strings.
		 The words file is a legacy UNIX file at /usr/share/dict/words that contains
		 a alphabetic list of about 200,000 common words. It's a flat, ASCII, text file.
		 
		 This is done only once, the first time a WordFinder operation is started.
		 */
		@synchronized(self) {
			if (dictionaryWords==nil) {
				NSMutableArray *allWords = [NSMutableArray new];
				
					// Read the entire "words" file into memory (crude, but effective)
				NSData			*wordsData = [NSData dataWithContentsOfFile:@"/usr/share/dict/words"];
				[wordsData retain];		// don't let NSData get garbage collected until the loop is done
				
				char			buffer[1024];				// 1K buffer used to construct C strings
				char			*t = buffer;				// pointer into buffer
				const char		*s = [wordsData bytes];		// pointer into source array
				unsigned int	count = [wordsData length];
				
				/*
				 Append the characters in wordsData to the word string buffer.
				 A newline character seperates words.
				 */
				while (count!=0)
				{
						// Get next character from source array
					count--;
					char c = *s++;
					
					if (c=='\n') {
							// newline: append accumulated word and reset buffer for next one
						NSString* word = [[NSString stringWithCString:buffer length:t-buffer] lowercaseString];
						if ([word length]>1)		// only add multicharacter words, becaue we all know a character is always a word!  
						{						
							[allWords addObject:word];
						}
						t = buffer;					// reset pointer to beginning of buffer
					}
					else if (c>' ') {
							// regular character
						*t++ = c;					// append character to buffer
					}
				}
				[wordsData release];
				dictionaryWords = [NSArray arrayWithArray:allWords];
			}
		}
	}
	return self;
}

- (void)awakeFromNib
{
	mode=1;
	
	/*
	 If user configures to show dock icon, supress the info.plist setting.
	 */
	if ([[NSUserDefaults standardUserDefaults] boolForKey:C8JShowDockIconKey]) {
		ProcessSerialNumber psn = { 0, kCurrentProcess };
			// display dock icon
		TransformProcessType(&psn, kProcessTransformToForegroundApplication);
			// enable menu bar
			//SetSystemUIMode(kUIModeNormal, 0);
		
			// switch to iSpeLL.app
		[[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:@"com.cool8jay.ispell" options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifier:nil];
			// switch back
		[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
	}
	
	[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
	
    [tableView setDoubleAction:@selector(openSpellResultItem:)];
    [tableView setTarget:self];
	[webView setFrameLoadDelegate:self];
	[webView setShouldCloseWithWindow:FALSE];
	[spellToolbar setSelectedItemIdentifier:@"searchItem"];	
	[modeTips setStringValue:@"Only letters and space are allowed!"];
	
	/*
	 By default, the working mode is "saerch", so set the search field to accept characters only.
	 */
	NSCharacterSet *alphabeticSet=[NSCharacterSet characterSetWithCharactersInString:C8JAlphabeticSpaceHyphenPeriodCharacters];
	[searchField setFormatter:[[SpellFormatter alloc] init]];
	[[searchField formatter] setAcceptableCharacterSet:alphabeticSet];
	
	/*
	 Setting icon for status item
	 */
	NSImage *onIcon = [NSImage imageNamed:@"S On.png"];
	NSImage *offIcon = [NSImage imageNamed:@"S Off.png"];
	
	/*
	 Build the statusbar menu	
	 */
    statusItem = [[[NSStatusBar systemStatusBar]statusItemWithLength:NSVariableStatusItemLength] retain];
    [statusItem setHighlightMode:YES];
    [statusItem setMenu:statusBarMenu];
	[statusItem setImage:offIcon];
	[statusItem setAlternateImage:onIcon];
    [statusItem setEnabled:YES];
	
	/*
	 Get curent "always on top" setting.
	 */
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];	
	BOOL alwaysOnTop= [defaults boolForKey:C8JAlwaysOnTopKey];	
	
	/*
	 Change window and menu property.
	 */
	
	if(alwaysOnTop)
	{
		[mainContentWindow setLevel:NSFloatingWindowLevel];
		[toggleAlwaysOnTopMenuItem setTitle:[NSString stringWithFormat:@"%C Always on top",0x2713]]; 
		[pinButton setState:NSPushInCellMask];
	}else {
		[mainContentWindow setLevel:NSNormalWindowLevel];		
		[toggleAlwaysOnTopMenuItem setTitle:[NSString stringWithFormat:@"Always on top"]]; 
		[pinButton setState:NSNoCellMask];
	}
	
	/*
	 Set web loading progress indicator
	 */
	[webLoadingProgressIndicator setForeColor:[NSColor whiteColor]];
	[webLoadingProgressIndicator setBackColor:[NSColor whiteColor]];
	
    [webLoadingProgressIndicator setDrawBackground:NO];
	[indicatorBox setHidden:YES];
	
	/*
	 Initialize preference controller and register shortcut for showing main window.
	 
	 The reason why the initialization must be here is that: the controlls in the preference controller will not be initialized
	 unless windowDidLoad method is called. Manual initialization of all controlls here will not work, because of custome view 
	 initilization mechanism, long story, check document online. See:
	 http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CocoaViewsGuide/SubclassingNSView/SubclassingNSView.html#//apple_ref/doc/uid/TP40002978-CH7-SW21
	 */
	preferenceController = [[PreferenceController alloc] initWithSpellController:self];
	
	unsigned int flags=[[[[NSUserDefaults standardUserDefaults] dictionaryForKey:C8JShowMainWindowShortcutKey] objectForKey:@"modifierFlags"] intValue];
	signed short code =	[[[[NSUserDefaults standardUserDefaults] dictionaryForKey:C8JShowMainWindowShortcutKey] objectForKey:@"keyCode"] intValue];
	
	[preferenceController registerHotkeyForShowingMainWindow:flags Code:code AtInit:TRUE];
}

- (void)openSpellResultItem:(id)sender
{
	int selectedRow=[tableView selectedRow];
	if(selectedRow==-1){
		return;
	}
	
	[contentView setString:@""];
	
	/*
	 Note, do not use NSString *clickedItem = [resultWords objectAtIndex:selectedRow];
	 See: http://cocoadev.com/forums/comments.php?DiscussionID=37&page=1
	 */
	NSString *clickedItem = [[wordsController arrangedObjects]objectAtIndex:selectedRow];
	
	NSMutableString *urlString =[[NSMutableString alloc] initWithString:@"http://www.dict.cn/"];
	[urlString appendString:clickedItem];
	[[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
}

#pragma mark -
#pragma mark Properties

- (NSString*)inputLetters
{
	return inputLetters;
}

- (void)setInputLetters:(NSString*)newLetters
{
	if (newLetters==nil)
		inputLetters = @"";
	else{
		inputLetters = newLetters;	
	}
}

#pragma mark -
#pragma mark Methods
- (IBAction)handleInput:(id)sender
{
	if(![self prepare]){
		return;
	}
	
	if (mode==1) {
		[self doSearch];
	}else if (mode==2) {
		[self doSpell];		
	}else if(mode==3){
		[self doCross];
	}
}

-(IBAction)switchToSearch:(id)sender{
	[resultDrawer close];
	NSCharacterSet *alphabeticAndSpaceSet=[NSCharacterSet characterSetWithCharactersInString:C8JAlphabeticSpaceHyphenPeriodCharacters];
	[[searchField formatter] setAcceptableCharacterSet:alphabeticAndSpaceSet];
	
	[modeTips setStringValue:@"Only letters and space are allowed!"];
	
	NSImage *offImage= [NSImage imageNamed:@"Chalkboard Off"];
	[spellOffToolbarItem setImage:offImage];
	[spellToolbar setSelectedItemIdentifier:@"searchItem"];
	
	mode=1;
}

-(IBAction)switchToSpell:(id)sender{
	[resultDrawer open];		
	
	NSCharacterSet *alphabeticSet=[NSCharacterSet characterSetWithCharactersInString:C8JAlphabeticCharacters];
	[[searchField formatter] setAcceptableCharacterSet:alphabeticSet];
	
	[modeTips setStringValue:@"Only letters are allowed!"];	
	
	NSImage *onImage= [NSImage imageNamed:@"Chalkboard On"];
	[spellOffToolbarItem setImage:onImage];
	[spellToolbar setSelectedItemIdentifier:@"spellItem"];
	
	mode=2;
}

-(IBAction)switchToCross:(id)sender{
	[resultDrawer open];
	NSCharacterSet *alphabeticAndQuestionAsteriskSet=[NSCharacterSet characterSetWithCharactersInString:C8JAlphabeticAndQuestionAsteriskCharacters];
	[[searchField formatter] setAcceptableCharacterSet:alphabeticAndQuestionAsteriskSet];
	
	[modeTips setStringValue:@"Only letters, \"?\" and \"*\" are allowed!\n"
	 @"? match 1 letter.\n"
	 @"* match 1 or more letters."
	 ];	
	
	NSImage *offImage= [NSImage imageNamed:@"Chalkboard Off"];
	[spellOffToolbarItem setImage:offImage];
	[spellToolbar setSelectedItemIdentifier:@"crossItem"];
	
	mode=3;
}

- (void)removeWords
{
		// Clear the entire list of found words
		// This message is sent when a WordFinder operation begins
	NSIndexSet *everyItemIndex = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,[resultWords count])];
	[wordsController removeObjectsAtArrangedObjectIndexes:everyItemIndex];
}

-(BOOL)prepare{
	trimmedInputLetters = [inputLetters stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
	
	if (trimmedInputLetters==nil||[trimmedInputLetters isEqualToString: @""])
	{
		return FALSE;
	}
	
	NSMutableString *tempMutableString = [NSMutableString stringWithString:inputLetters];
	[tempMutableString replaceOccurrencesOfString:@" " withString:@"+" options:0 range:NSMakeRange(0, [tempMutableString length])];
	trimmedInputLetters=tempMutableString;
	return TRUE;
}

-(void)doSearch{
	[[wordsController content] removeAllObjects];
	NSMutableString *urlString =[[NSMutableString alloc] initWithString:@"http://www.dict.cn/"];
	[urlString appendString:trimmedInputLetters];
	NSURL *url=[NSURL URLWithString:urlString];
	NSURLRequest *urlRequest=[NSURLRequest requestWithURL:url];
	[[webView mainFrame] loadRequest:urlRequest];
}

-(void)doCross{
	[[wordsController content] removeAllObjects];
	[spellingProgressIndicator startAnimation:nil];
	NSPredicate *predicate; 
	predicate = [NSPredicate predicateWithFormat: @"SELF like[cd] %@",trimmedInputLetters];
	NSArray *results = [dictionaryWords filteredArrayUsingPredicate: predicate];
	[[wordsController content]addObjectsFromArray:results];
	
	if ([lengthMatters state]==NSOnState){
		NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"length" ascending:NO];  
		[wordsController setSortDescriptors:[NSArray arrayWithObject:sort]];  		
	}else{
		NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"description" ascending:YES];  
		[wordsController setSortDescriptors:[NSArray arrayWithObject:sort]]; 		
	}
	
	[wordsController rearrangeObjects];
	
	int min=[trimmedInputLetters length];
	NSNumber *theMax = [[wordsController content] valueForKeyPath:@"@max.length"];
	int crossResultMaxLength=[theMax intValue];
	
	[slider setMinValue:min];
	[slider setMaxValue:crossResultMaxLength];
	[slider setNumberOfTickMarks:crossResultMaxLength-1];
	[slider setIntValue:crossResultMaxLength];
	
	NSString *attributeName = @"length";
	NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"%K >= %d", attributeName, crossResultMaxLength];	
	[wordsController setFilterPredicate:filterPredicate];
	
	int amountOfResult=[[wordsController arrangedObjects]count];
	
	NSString *resultVauleAsString=[NSString stringWithFormat:@"%d words not shorter than %d:",amountOfResult,crossResultMaxLength];
	[resultLabel setStringValue:resultVauleAsString];
	
	[spellingProgressIndicator stopAnimation:nil];
}

-(void)doSpell{
	[self beforeSpell];
	
	[spellingProgressIndicator startAnimation:nil];
	
		// Whenever the letter set changes, start a new search
		// operation to find the words that can be spelled.
	[finderQueue cancelAllOperations];				// stop any searches that are in progress
	WordFinder *finder = [[WordFinder alloc] initWithLetters:inputLetters controller:self];
	[finderQueue addOperation:finder];				// start searching using this letter set
}

-(void)beforeSpell{
	[[wordsController content] removeAllObjects];
	int alphabeticLength=[trimmedInputLetters length];
	[slider setMinValue:2];
	[slider setMaxValue:alphabeticLength];
	[slider setNumberOfTickMarks:alphabeticLength-1];
	[slider setIntValue:alphabeticLength];
}

/*
 Preparation work before spelling.
 1. clean content view for new word.
 2. empty spelled words.
 3. reset slider.
 4. analyse input string and update UI.
 */
	//-(void)beforeSpell:(NSString*) string{
	//	[[wordsController content] removeAllObjects];
	//	
	//	[slider setMaxValue:2];
	//	[slider setNumberOfTickMarks:1];
	//	[slider setIntValue:2];
	//	[minimumLimitLabel setIntValue:2];
	//	
	//	NSCharacterSet *alphabeticSet=[NSCharacterSet characterSetWithCharactersInString:C8JAlphabeticCharacters];
	//	
	//	
	//	NSCharacterSet* nonAlphaNumericSet = [alphabeticSet invertedSet];
	//	if ([string rangeOfCharacterFromSet:nonAlphaNumericSet].location != NSNotFound)
	//	{
	//		return;
	//	}
	//	
	//	NSScanner *scanner = [NSScanner scannerWithString:string];
	//	
	//	NSString *temp;
	//	
	//	NSMutableString *filteredString=[[NSMutableString alloc] initWithString:@""];
	//	
	//	while(![scanner isAtEnd])
	//	{
	//		[scanner scanCharactersFromSet:alphabeticSet intoString:&temp];
	//		[filteredString appendString:temp];
	//		[scanner scanUpToCharactersFromSet:alphabeticSet intoString:nil];
	//	}
	//	
	//	int alphabeticLength=[filteredString length];
	//	
	//	[slider setMaxValue:alphabeticLength];
	//	[slider setNumberOfTickMarks:alphabeticLength-1];
	//	[slider setIntValue:alphabeticLength];
	//	[minimumLimitLabel setIntValue:alphabeticLength];
	//}

/*
 This message is sent whenever the WordFinder operation finds another new word.
 Add a new word into the list, but do not add duplicates.
 */
- (IBAction)foundWord:(NSString*)word
{
		// If we check the checkbox on the application interface, which means we hope to sort the result by length in 
		// descending order(from the longest words to the shortest words, if two same length met, then sorted in 
		// alphabetical ascending order).
	if ([lengthMatters state]==NSOnState){
		NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"length" ascending:NO];  
		[wordsController setSortDescriptors:[NSArray arrayWithObject:sort]];  		
	}else{
		NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"description" ascending:YES];  
		[wordsController setSortDescriptors:[NSArray arrayWithObject:sort]]; 		
	}
	
	/* 
	 Words are added in sorted order, so the duplicate check only needs to test the last item in the array.
	 
	 This is an optimization, imagine the first time we add a word, we do not need to check the duplication, only one thing
	 can not make duplication. Then we add another new word, we have two words, compare them! If duplicated, reject to
	 add. If not duplicated, continue. Now we add the third one, we only need to compare it with the second one, because we
	 already know that the two existed words are different, if the third is different from the second, it must be different 
	 from the first!
	 */
	if ([resultWords count]==0 || ![[resultWords lastObject] isEqualTo:word]){
		[wordsController addObject:word];
	}
	
	int value=[slider intValue];
	NSString *attributeName = @"length";
	NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"%K >= %d", attributeName, value];	
	[wordsController setFilterPredicate:filterPredicate];
	
	[wordsController rearrangeObjects];
	
	int size=[[wordsController arrangedObjects]count];
	int currentSliderValue=[slider intValue];
	NSString *resultVauleAsString=[NSString stringWithFormat:@"%d words not shorter than %d:",size,currentSliderValue];
	[resultLabel setStringValue:resultVauleAsString];
}

- (void) fillInContentView{
		// reset content of text view
	[contentView setString:@""];	
	
	NSMutableDictionary *wordExampleTitleAttributes=[Dict_cn_Style word_Example_Title_Attributes];
	NSMutableDictionary *wordExampleContentAttributes=[Dict_cn_Style word_Example_Content_Attributes];
	NSMutableDictionary *noWordFoundAttributes=[Dict_cn_Style no_Word_Found_Attributes];
	NSMutableDictionary *wordNameAttributes=[Dict_cn_Style word_Name_Attributes];
	NSMutableDictionary *wordPronunciationAttributes=[Dict_cn_Style word_Pronunciation_Attributes];
	
	NSMutableAttributedString *carriageSymbol=[[NSMutableAttributedString alloc] initWithString:@"\n"];
	NSMutableAttributedString *newLineString=[[NSMutableAttributedString alloc] initWithString:@"\n\n"];
	
	/*
	 Displaying step:
	 1. word name(must exist)
	 2. word pronunciation(if exists)
	 3.1 a list of Chinese word explannations(must exist, otherwise no word found!)
	 3.2 if no word found, check if there are guess words
	 4. a list of Chinese word examples(if exists)
	 5. a list of English word explannations(if exists)
	 */
	DOMDocument	*domDocument=[[webView mainFrame] DOMDocument];
	/*
	 Check if the input word exist.
	 */
	if ([domDocument getElementById:@"word-exp-block"]==nil) {
		if([domDocument getElementById:@"word-guess-block"]!=nil){
			NSString *wordGuess=[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById('word-guess-block').innerHTML"];
			NSString* guessRegex1 = @"\">((.|\\s)*?)</a></li>";
			
			NSArray *guessArray=[wordGuess componentsMatchedByRegex:guessRegex1];
			int sizeOfGuess=[guessArray count];
			
			if(sizeOfGuess>0){
				NSString *guessTip=@"Do you want to search for these:\n\n";
				[[contentView textStorage] setAttributedString:[[NSMutableAttributedString alloc] 
																initWithString:guessTip
																attributes:noWordFoundAttributes]];
				
				for(int a=0;a<sizeOfGuess;a++){
					NSString* guessRegex2 = @"\">((.|\\s)*?)</a></li>";
					NSString *tidyItem=	[[guessArray objectAtIndex:a] stringByMatching:guessRegex2 capture:1];
					NSString *temp;
					if(a!=sizeOfGuess-1){
						temp = [NSString stringWithFormat:@"%d. %@\n\n",a+1,tidyItem];			
					}else {
						temp = [NSString stringWithFormat:@"%d. %@\n",a+1,tidyItem];							
					}
					
					NSMutableAttributedString *tmp=[[NSMutableAttributedString alloc] initWithString:temp attributes:wordExampleContentAttributes];
					[[contentView textStorage] appendAttributedString:tmp];	
				}
				
				NSMutableArray *tidyGuessArray = [NSMutableArray arrayWithCapacity:[guessArray count]]; // needs to be mutable
				
				int count = [tidyGuessArray count];
				for (int i = 0; i < count; i++) {
					NSString *ojb1 = [tidyGuessArray objectAtIndex:i];
					NSString *ojb2 = [tidyGuessArray objectAtIndex:i];
					NSString *temp;
					if(i!=count-1){
						temp = [NSString stringWithFormat:@"%d. %@\n    %@\n\n",i+1,ojb1,ojb2];			
					}else {
						temp = [NSString stringWithFormat:@"%d. %@\n    %@\n\n",i+1,ojb1,ojb2];							
					}
					
					NSMutableAttributedString *tmp=[[NSMutableAttributedString alloc] initWithString:temp attributes:wordExampleContentAttributes];
					[[contentView textStorage] appendAttributedString:tmp];	
				}
				return;
			}
		}
		/*
		 No guess word
		 */
		
		NSString *warning=@"No word found!";
		[[contentView textStorage] setAttributedString:[[NSMutableAttributedString alloc] 
														initWithString:warning
														attributes:noWordFoundAttributes]];
		
		return;
	}
	
	/*
	 1. word name(must exist)
	 */
	NSString *wordName=[[domDocument getElementById:@"word"]textContent];
	
	NSMutableAttributedString *attributedWordName=[[NSMutableAttributedString alloc] initWithString:wordName 
																						 attributes:wordNameAttributes];
	
	[[contentView textStorage] appendAttributedString:attributedWordName];
	
	/*
	 2. word pronunciation(if exists)
	 */
	if([domDocument getElementById:@"pron"]!=nil){
		NSString *wordPronunciation=[[domDocument getElementById:@"pron"]textContent];
		
		NSMutableAttributedString *attributedWordPronunciation = [[NSMutableAttributedString alloc] initWithString:wordPronunciation
																										attributes:wordPronunciationAttributes];
		NSMutableAttributedString *spaceString3=[[NSMutableAttributedString alloc] initWithString:@"   "];
		
		[[contentView textStorage] appendAttributedString:spaceString3];
		[[contentView textStorage] appendAttributedString:attributedWordPronunciation];
	}
	
	/*
	 3. word explannation(must have, otherwise no word found!)	 
	 */
		// A way to get "innerHTML"
	[[contentView textStorage] appendAttributedString:newLineString];
	
	NSString *wordExplannation=[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById('word-exp-block').innerHTML"];//word explanation
	
	wordExplannation=[wordExplannation stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
	
	NSMutableString *tempMutableString = [NSMutableString stringWithString:wordExplannation];
	
	[tempMutableString replaceOccurrencesOfString:@"<br>" withString:@"\n" options:0 range:NSMakeRange(0, [tempMutableString length])];
	[tempMutableString replaceOccurrencesOfString:@"<font style=\"color:#666;\">" withString:@"" options:0 range:NSMakeRange(0, [tempMutableString length])];
	[tempMutableString replaceOccurrencesOfString:@"</font>" withString:@"" options:0 range:NSMakeRange(0, [tempMutableString length])];
	
	[tempMutableString replaceOccurrencesOfString:@"&lt;" withString:@"<" options:0 range:NSMakeRange(0, [tempMutableString length])];
	[tempMutableString replaceOccurrencesOfString:@"&gt;" withString:@">" options:0 range:NSMakeRange(0, [tempMutableString length])];
	
	wordExplannation=tempMutableString; 
	
	NSMutableAttributedString *attributedWordExplannation=[[NSMutableAttributedString alloc] initWithString:wordExplannation];	
	
	[[contentView textStorage] appendAttributedString:attributedWordExplannation];
	
	/*
	 4. Chinese word example(if exists)
	 */
	if ([domDocument getElementById:@"exp_eg"]!=nil) {
			//[[contentView textStorage] appendAttributedString:newLineString];
		
		DOMHTMLElement *htmlNode = (DOMHTMLElement *)[domDocument getElementById:@"exp_eg"];
		
		NSString *chineseExampleSource=[htmlNode innerHTML];
		NSString* titleRegex = @"<h3>(.*?)<img src=";
		NSString* exampleTitle = [chineseExampleSource stringByMatching:titleRegex capture:1];
		
		NSString* regex = @"<li>((.|\\s)*?)</li>";
		NSArray *exampleArray=[chineseExampleSource componentsMatchedByRegex:regex];
		
		NSMutableArray *myEnglishExampleArray = [NSMutableArray arrayWithCapacity:[exampleArray count]]; // needs to be mutable
		NSMutableArray *myChineseExampleArray = [NSMutableArray arrayWithCapacity:[exampleArray count]]; // needs to be mutable
		
		for (int x=0; x<[exampleArray count];x++)
		{
			NSString *dirtyExampleString=[exampleArray objectAtIndex:x];
			
			NSString* regex1 = @"<li>((.|\\s)*?)<a";
			NSString* regex2 = @"<p>((.|\\s)*?)</p>";
			NSString *englishItem=	[dirtyExampleString stringByMatching:regex1 capture:1];
			NSString *chineseItem=	[dirtyExampleString stringByMatching:regex2 capture:1];
			
			NSMutableString *tempMutableString = [NSMutableString stringWithString:englishItem];
			NSMutableString *tempMutableString2 = [NSMutableString stringWithString:chineseItem];
			
			[tempMutableString replaceOccurrencesOfString:@"<em>" withString:@"" options:0 range:NSMakeRange(0, [tempMutableString length])];
			[tempMutableString replaceOccurrencesOfString:@"</em>" withString:@"" options:0 range:NSMakeRange(0, [tempMutableString length])];
			[tempMutableString replaceOccurrencesOfString:@"\t" withString:@"" options:0 range:NSMakeRange(0, [tempMutableString length])];
			
			[tempMutableString replaceOccurrencesOfString:@"&lt;" withString:@"<" options:0 range:NSMakeRange(0, [tempMutableString length])];
			[tempMutableString replaceOccurrencesOfString:@"&gt;" withString:@">" options:0 range:NSMakeRange(0, [tempMutableString length])];
			[tempMutableString2 replaceOccurrencesOfString:@"&lt;" withString:@"<" options:0 range:NSMakeRange(0, [tempMutableString2 length])];
			[tempMutableString2 replaceOccurrencesOfString:@"&gt;" withString:@">" options:0 range:NSMakeRange(0, [tempMutableString2 length])];
			
			[myEnglishExampleArray addObject:[NSString stringWithString:tempMutableString]];
			[myChineseExampleArray addObject:[NSString stringWithString:tempMutableString2]];
		}
		
		NSMutableAttributedString *attributedWordExampleTitle=[[NSMutableAttributedString alloc] initWithString:exampleTitle attributes:wordExampleTitleAttributes];
		
		[[contentView textStorage] appendAttributedString:newLineString];
		[[contentView textStorage] appendAttributedString:attributedWordExampleTitle];	
		[[contentView textStorage] appendAttributedString:carriageSymbol];	
		
		int count = [myEnglishExampleArray count];
		for (int i = 0; i < count; i++) {
			NSString *ojb1 = [myEnglishExampleArray objectAtIndex:i];
			NSString *ojb2 = [myChineseExampleArray objectAtIndex:i];
			NSString *temp;
			if(i!=count-1){
				temp = [NSString stringWithFormat:@"%d. %@\n    %@\n\n",i+1,ojb1,ojb2];			
			}else {
				temp = [NSString stringWithFormat:@"%d. %@\n    %@",i+1,ojb1,ojb2];							
			}
			
			NSMutableAttributedString *tmp=[[NSMutableAttributedString alloc] initWithString:temp attributes:wordExampleContentAttributes];
			[[contentView textStorage] appendAttributedString:tmp];	
		}
	}
	
	/*
	 5. word english explannation(if exists)
	 */
	if([domDocument getElementById:@"exp_eee"]!=nil){
			//	[self fillEnglishExplanation:domDocument];
	}
}
	//TODO: fix performance problem
-(void)fillEnglishExplanation:(DOMDocument*)domDocument{
		//NSMutableDictionary *wordExampleTitleAttributes=[Dict_cn_Style word_Example_Title_Attributes];
	NSMutableDictionary *wordExampleContentAttributes=[Dict_cn_Style word_Example_Content_Attributes];
		//NSMutableDictionary *noWordFoundAttributes=[Dict_cn_Style no_Word_Found_Attributes];
		//NSMutableDictionary *wordNameAttributes=[Dict_cn_Style word_Name_Attributes];
		//NSMutableDictionary *wordPronunciationAttributes=[Dict_cn_Style word_Pronunciation_Attributes];
	
		//NSMutableAttributedString *carriageSymbol=[[NSMutableAttributedString alloc] initWithString:@"\n"];
	NSMutableAttributedString *newLineString=[[NSMutableAttributedString alloc] initWithString:@"\n\n"];
	
	/*
	 5. word english explannation(if exists)
	 */
	if ([domDocument getElementById:@"exp_eee"]!=nil) {
		
		[[contentView textStorage] appendAttributedString:newLineString];
		
		DOMHTMLElement *htmlNode = (DOMHTMLElement *)[domDocument getElementById:@"exp_eee"];
		NSString *chineseExampleSource=[htmlNode innerHTML];
		NSString* englishExampleBlockRegex = @"<h4>((.|\\s)*?)</ol>";
		
		NSArray *englishExampleBlockArray=[chineseExampleSource componentsMatchedByRegex:englishExampleBlockRegex];
		
		for(int a=0;a<[englishExampleBlockArray count];a++){
			BOOL titleShownFlag=FALSE;
			NSString *chineseExampleSource=[englishExampleBlockArray objectAtIndex:a];
			NSString *englishPartOfSpeechTitleRegex=@"<h4>((.|\\s)*?)</h4>";
			NSString *englishPartOfSpeechTitle=[chineseExampleSource stringByMatching:englishPartOfSpeechTitleRegex capture:1];
			
			if ([englishPartOfSpeechTitle rangeOfString: @"名词"].location!=NSNotFound) {
				englishPartOfSpeechTitle=@"Noun\n";
			}
			if ([englishPartOfSpeechTitle rangeOfString: @"动词"].location!=NSNotFound) {
				englishPartOfSpeechTitle=@"Verb\n";
			}
			if ([englishPartOfSpeechTitle rangeOfString: @"形容词"].location!=NSNotFound) {
				englishPartOfSpeechTitle=@"Adjective\n";
			}
			if ([englishPartOfSpeechTitle rangeOfString: @"副词"].location!=NSNotFound) {
				englishPartOfSpeechTitle=@"Adverb\n";
			}
			if ([englishPartOfSpeechTitle rangeOfString: @"介词"].location!=NSNotFound) {
				englishPartOfSpeechTitle=@"Preposition\n";
			}
			
			NSString *eeRegex=@"<li>((.|\\s)*?)</li>";
			NSArray *eeeArray=[chineseExampleSource componentsMatchedByRegex:eeRegex];
			
			int acount=[eeeArray count];
			for(int b=0;b<acount;b++){				
				
				if(!titleShownFlag){
					NSMutableString *tempMutableString33=[NSString stringWithFormat:@"%@\n",englishPartOfSpeechTitle];
					
					NSMutableAttributedString *tmp=[[NSMutableAttributedString alloc] initWithString:tempMutableString33 attributes:wordExampleContentAttributes];
					[[contentView textStorage] appendAttributedString:tmp];	
					titleShownFlag=TRUE;
				}
				
				NSString *item=[eeeArray objectAtIndex:b];
				NSString *eeContentRegex=@"<li>((.|\\s)*?)<div>";
				NSString *itemWithoutThesaurus=[item stringByMatching:eeContentRegex capture:1];
				if(itemWithoutThesaurus!=nil){
					item=itemWithoutThesaurus;
				}else{
					NSString *normalItemMatcher=@"<li>((.|\\s)*?)</li>";
					NSString *normalItem=[item stringByMatching:normalItemMatcher capture:1];
					item=normalItem;
				}
				
				NSMutableString *tempMutableString;
				if(b!=acount-1){
					tempMutableString = [NSString stringWithFormat:@"%d. %@\n\n",b+1,item];			
				}else {
					tempMutableString = [NSString stringWithFormat:@"%d. %@\n\n",b+1,item];							
				}			
				NSMutableAttributedString *tmp=[[NSMutableAttributedString alloc] initWithString:tempMutableString attributes:wordExampleContentAttributes];
				[[contentView textStorage] appendAttributedString:tmp];
			}
		}
	}
}

/*
 * Sort spelled words, because most of the time we care about the longest word most.
 */
- (IBAction)sortWords:(id)sender
{
	if ([lengthMatters state]==NSOnState){
		NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"length" ascending:NO];  
		[wordsController setSortDescriptors:[NSArray arrayWithObject:sort]];  		
	}else{
		NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"description" ascending:YES];  
		[wordsController setSortDescriptors:[NSArray arrayWithObject:sort]]; 		
	}
	[wordsController rearrangeObjects];		
}

/*
 * Filter spelled words, because we always care about the long words, the the short words.
 */
- (IBAction)filterWords:(id)sender{
	int value=[slider intValue];
	NSString *attributeName = @"length";
	NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"%K >= %d", attributeName, value];	
	[wordsController setFilterPredicate:filterPredicate];
	
	int size=[[wordsController arrangedObjects]count];
	int currentSliderValue=[slider intValue];
	NSString *resultVauleAsString=[NSString stringWithFormat:@"%d words not shorter than %d:",size,currentSliderValue];
	[resultLabel setStringValue:resultVauleAsString];
}

-(void)report:(id)sender{
	NSString *subject=@"I think that iSpeLL is...";
	NSString *body=@"Because ...";
	NSString *to=@"cool8jay@qq.com";
	
	NSString *encodedSubject = [NSString stringWithFormat:@"SUBJECT=%@", [subject stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
	NSString *encodedBody = [NSString stringWithFormat:@"BODY=%@", [body stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
	NSString *encodedTo = [to stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	NSString *encodedURLString = [NSString stringWithFormat:@"mailto:%@?%@&%@", encodedTo, encodedSubject, encodedBody];
	NSURL *mailtoURL = [NSURL URLWithString:encodedURLString];
	[[NSWorkspace sharedWorkspace] openURL:mailtoURL];
}

- (IBAction)openHelp:(id)sender
{
	NSString *urlString = @"http://code.google.com/p/ispellformac/";
	NSURL *url = [NSURL URLWithString:urlString];
    [[NSWorkspace sharedWorkspace] openURL:url];
}

- (IBAction)showPreferencePanel:(id)sender
{
	[preferenceController showWindow:self];
}


- (IBAction)toggleAlwaysOnTop:(id)sender
{
	/*
	 Get curent "always on top" setting.
	 */
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];	
	BOOL alwaysOnTop= [defaults boolForKey:C8JAlwaysOnTopKey];	
	/*
	 Change window and menu property.
	 */
	if(alwaysOnTop)
	{
		[mainContentWindow setLevel:NSNormalWindowLevel];		
		[toggleAlwaysOnTopMenuItem setTitle:[NSString stringWithFormat:@"Always on top"]]; 
		[pinButton setState:NSNoCellMask];
	}else {
		[mainContentWindow setLevel:NSFloatingWindowLevel];
		[toggleAlwaysOnTopMenuItem setTitle:[NSString stringWithFormat:@"%C Always on top",0x2713]]; 
		[pinButton setState:NSPushInCellMask];
	}
	
	/*
	 Set reversed value to the user defaults;
	 */
	[defaults setBool:!alwaysOnTop forKey:C8JAlwaysOnTopKey];
}

#pragma mark -
#pragma mark WebFrameLoadDelegate Methods

- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
	[indicatorBox setHidden:NO];
	[webLoadingProgressIndicator startAnimation:self];
}

- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
	[indicatorBox setHidden:YES];
	[webLoadingProgressIndicator stopAnimation:self];
	[self fillInContentView];
}

- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
{
	if (error.code == NSURLErrorCancelled) return; // this is Error -999
												   // error handling for "real" errors here	
	[indicatorBox setHidden:YES];
	[webLoadingProgressIndicator stopAnimation:self];
	NSAlert *alert = [NSAlert alertWithError:error];
	[alert runModal];
}

- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
{
		// Ignore this error, see: http://discussions.apple.com/thread.jspa?messageID=9210463
	if (error.code == NSURLErrorCancelled) return; // this is Error -999
												   // error handling for "real" errors here
}

#pragma mark -
#pragma mark NSApplicationDelegate protocol methods

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
	return FALSE;
}

/*
 Reopen the closed application, the application only has one
 window, so -makeKeyAndOrderFront: and -orderFront: have same
 works same in this situation. 
 */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication
					hasVisibleWindows:(BOOL)flag
{	
	[mainContentWindow makeKeyAndOrderFront:nil];
	return TRUE;
}

#pragma mark -
#pragma mark NSWindowDelegate protocol methods

- (void)windowDidBecomeMain:(NSNotification *)notification
{
	BOOL alwaysOnTop= [[NSUserDefaults standardUserDefaults] boolForKey:C8JAlwaysOnTopKey];	
	
	if (alwaysOnTop) {
		[mainContentWindow setLevel:NSFloatingWindowLevel];
	} else {
		[mainContentWindow setLevel:NSNormalWindowLevel];
	}
}

@end